//! 响应式原理 reactive 的实现 P106
const obj = {};
const proto = {bar:1};
const child = reactive(obj);
const parent = reactive(proto);
// 使用 parent 作为 child 的原型
Object.setPrototypeOf(child,parent);
effect(()=>{
  console.log(child.bar); //1
})
// 修改 child.bar 的值
child.bar = 2 // 会执行两次副作用，原因是 set 的时候 child.bar parent.bar 都与副作用函数建立了联系，在赋值的时候， 调用 [[get]] 时，实际上是调用了两次get  
function reactive(obj){
  return new Proxy(obj,{
    set(target,key,newVal,receiver){
      const oldVal = target[key];
      // 判断设置的属性是否是原有的，不是自身拥有的就是新增
      const type = Object.prototype.hasOwnProperty.call(target,key) ? 'SET' : 'ADD' ;
      const res = Reflect.set(target,key,newVal,receiver);
      // target === receiver.raw 说明 receiver 就是target 的代理对象， 如果 target 是沿着原型链找到的原型对象 如上面例子中的 parent， 则不等，这里 reveiver 一直都是代理对象 如 child
      if(target === receiver.raw){
        // && 后面用于判断 NaN 的情况，因为 NaN === NaN  为false 
        if(oldVal !== newVal && (oldVal === oldVal || newVal === newVal)){
          tirgger(target,key,type)
        }
      }
      return res
    },
    get(target,key,receiver){
      // 代理对象可以通过raw属性 访问原始数据
      if(key === 'raw'){
        return target
      }

      track(target,key)
      return Reflect.get(target,key,receiver)
    }
  })
}

// ref 源码 value 可以是 原始值，Object，Array，Map,Set等 实际上就是返回一个实例对象， 实例对象就是 RefImpl 类
function ref(value){
  return createRef(value)
}
// shallow 表示是否是浅响应
function createRef(rawValue,shallow = false){
  // 判断是否已是ref 对象
  if(ifRef(rawValue)){
    return rawValue
  }
  return new RefImpl(rawValue,shallow)
}
// 三点核心： value属性的getter，value属性的setter，shallow与convert
// convert 实现如下： 如果值是对象类型就会调用reactive实现完全代理 ，传入给ref的值是对象，实际上内部会调用reactive来实现完全代理，即value属性的值也会被代理，可以配置第二个参数shallow为 true，则不进行完全代理，只浅响应
const convert = (val) => isObject(val) ? reactive(val) : val; 
class RefImpl{
  constructor(_rawValue,_shallow){
    this._rawValue = _rawValue; // 原始数据
    this._shallow = _shallow; // 是否是浅层代理
    this.__v_isRef = true; // 标记ref对象
    this._value = _shallow ? _rawValue : convert(_rawValue); // 返回的 ref 对象
  }
  // 依赖追踪(收集依赖)对target进行toRaw操作，即始终使用目标代理的原始对象，而非代理对象。
  get value(){
    track(toRaw(this),'get' /* GET */, 'value')
    return this._value
  }
  // 调用trigger函数来触发视图的更新(触发副作用)，其次会替换内部属性_value的值为最新的值。
  set value(newVal){ 
    if(hasChanged(toRaw(newVal),this._rawValue)){
      this._rawValue = newVal;
      this._value = this._shallow ? newVal : convert(newVal);
      tirgger(toRaw(this),'set' /* SET */, 'value', newVal)
    }
  }
}
// 收集依赖
function track()
// 派发更新



// toRef：可以用来为源响应式对象上的 property 创建一个 ref。然后可以将 ref 传递出去，从而保持对其源 property 的响应式连接 语法是：toRef(Object, key) 实际上toRef只能用来对指定的对象属性创建一个ref对象。
// 如果对象属性的值本身就是ref对象就不会再次处理，否则会调用ObjectRefImpl类创建对应实例。ObjectRefImpl中不会对属性做任何操作，仅仅是定义value属性，而value属性的getter、setter只是简单的获取和赋值操作，toRef只是定义了value属性而已，之所以实现保持对 对象源属性的响应式连接，完全是属性所在对象自身必须是响应式
class ObjectRefImpl{
  constructor(_object,_key){
    this._object = _object;
    this._key = _key;
    this.__v_isRef = true;
  }
  get value(){
    return this._object[this._key]
  }
  set value(newVal){
    this._object[this._key] = newVal
  }
}
function toRef(object,key){
  return isRef(object[key]) ? object[key] : new ObjectRefImpl(object,key)
}
// toRefs: 就是批量的执行 toRef， 支持数组类型，只是浅层处理，只处理对象自身属性，不处理嵌套属性
function toRefs(object){
  //object 必须是代理对象
  if(!isProxy(object)){
    console.warn(`toRefs() expects a reactive object but received a plain one.`)
  }
  const ret = isArray(object) ? new Array(object.length) : { };
  for(const key in object) {
    ret[key] = toRef(object,key)
  }
  return ret
}

//! 自动脱ref ， 如果读取的属性是一个ref，则直接将ref对应的value属性值返回，这样就完成了在模板中直接使用变量读取，不需要.value 来读取 ref, reactive 函数自身带有自动脱ref 的能力
function proxyRefs(target){
  return new Proxy(target,{
    get(target,key,receiver){
      const value = Reflect.get(target,key,receiver);
      // 自动脱 ref 实现： 如果读取的是 ref， 则返回他的 value 属性值，上面的 ref 实现中，每个ref都有一个ref属性标记 __v_isRef
      return value.__v_isRef ? value.value : value
    },
    set(target,key,newVal,receiver){
      // 通过 target 读取真实值
      const value = target[key];
      // 如果值是 Ref， 则设置其对应的value属性值
      if(value.__v_isRef){
        value.value = newVal
        return true
      }
      return Reflect.set(target,key,newVal,receiver)
    }
  })
}



